package com.incar.chuxing.ble.manager;

import static com.incar.chuxing.ble.utils.ConvertUtil.getIntFrom2ByteArray;
import static com.incar.chuxing.ble.utils.HexUtil.encodeHexStr;

import java.util.Arrays;
import java.util.concurrent.LinkedBlockingDeque;

import com.google.gson.Gson;
import com.incar.chuxing.ble.bean.CommandBleDbBean;
import com.incar.chuxing.ble.bean.ControlResult;
import com.incar.chuxing.ble.helper.IParseResultCallback;
import com.incar.chuxing.ble.utils.ArrayUtil;
import com.incar.chuxing.ble.utils.ConvertUtil;
import com.incar.chuxing.ble.utils.HexUtil;
import com.incar.chuxing.ble.utils.Logger;

/**
 * Created by xiongjida on 2017/6/28.
 */

/**
 * 管理BLE所有指令的解析
 */
public class ParseManager {

    private volatile static ParseManager instance;

    // 保存所有已发送指令流水号
    private LinkedBlockingDeque<ControlResult> mControlCmds = new LinkedBlockingDeque<>();

    private LinkedBlockingDeque<byte[]> lastQuen = new LinkedBlockingDeque<>();

    private int currentOBDSerialNum = -1;

    private byte[] temp = new byte[]{};

    private ParseManager() {
    }

    public static ParseManager getInstance() {
        if (instance == null) {
            synchronized (ParseManager.class) {
                if (instance == null) {
                    instance = new ParseManager();
                }
            }
        }
        return instance;
    }

    /**
     * 数据组包处理，收到数据后就调用此方法 蓝牙底层会自动组包
     *
     * @param bytes
     */
    public synchronized void parse(byte[] bytes, IParseResultCallback mParseCallback) {
        if (mParseCallback == null) {
            throw new RuntimeException("未设置回调函数");
        }
        if (bytes == null) {
            return;
        }

        if (Arrays.equals(temp, bytes)) {
            return;
        } else {
            temp = bytes;
        }

        if (!lastQuen.isEmpty()) {
            bytes = ConvertUtil.byteMerger(lastQuen.poll(), bytes);
        }

        int length = bytes.length;
        int index = ArrayUtil.indexOf(bytes, new byte[]{0x7E, 0x10});
        LinkedBlockingDeque<Integer> quen = new LinkedBlockingDeque<>();
        byte[] last = bytes;
        while (index != -1) {
            int lastLength = last.length;
            quen.offer(index + length - lastLength);
            last = Arrays.copyOfRange(last, index + 2, lastLength);
            index = ArrayUtil.indexOf(last, new byte[]{0x7E, 0x10});
        }

        int lastIndex = 0;
        if (!quen.isEmpty()) {
            int first = quen.poll();
            lastIndex = first;
            while (!quen.isEmpty()) {
                int second = quen.poll();
                if (quen.isEmpty()) {
                    lastIndex = second;
                }
                byte[] b = Arrays.copyOfRange(bytes, first, second);
                int bagLength = b.length;
                if (isRightPacket(b)) {
                    if (bagLength > 5) {
                        byte[] serialNum = new byte[]{b[4], b[5]};
                        int serilalOBDNum = getIntFrom2ByteArray(serialNum);
                        if (currentOBDSerialNum < serilalOBDNum) {
                            currentOBDSerialNum = serilalOBDNum;
                            receiveData(b, mParseCallback);
                        }
                    }
                }
                first = second;
            }
        }

        bytes = Arrays.copyOfRange(bytes, lastIndex, length);

        if (bytes[0] == 0x7E && bytes[1] == 0x10) {
            length = bytes.length;
            if (length > 3) {
                int bufferLength = ConvertUtil.bytesToIntHigh(
                        new byte[]{0, 0, bytes[2], bytes[3]}, 0) + 4;
                if (length >= bufferLength) {
                    // 解析
                    byte[] b = Arrays.copyOfRange(bytes, 0, bufferLength);
                    int bagLength = b.length;
                    if (isRightPacket(b)) {
                        if (bagLength > 5) {
                            byte[] serialNum = new byte[]{b[4], b[5]};
                            int serilalOBDNum = getIntFrom2ByteArray(serialNum);
                            if (currentOBDSerialNum < serilalOBDNum) {
                                currentOBDSerialNum = serilalOBDNum;
                                receiveData(b, mParseCallback);
                            }
                        }
                    }
                } else {
                    lastQuen.offer(bytes);
                }
            } else {
                lastQuen.offer(bytes);
            }
        }
    }

    /**
     * 包是否完整,crc校验
     *
     * @param data
     * @return
     */

    private boolean isRightPacket(byte[] data) {
        // crc校验
        int dataLength = data.length;
        if (dataLength > 4) {
            byte[] body = Arrays.copyOfRange(data, 4, data.length);
            int length = body.length;
            if (length > 2) {
                byte[] crc = new byte[]{body[length - 2], body[length - 1]};
                byte[] content = Arrays.copyOfRange(body, 0, length - 2);
                int crc_xw = ConvertUtil.checkCRC_XW(content, 0, length - 2);
                byte[] crcByte = ConvertUtil.shortToBytes((short) crc_xw);

                if (Arrays.equals(crc, crcByte)) {
                    return true;
                }
            }
        }
        return false;
    }

    // 数据处理
    private void receiveData(byte[] data, IParseResultCallback mParseCallback) {
        // 处理组包后的数据
        String s = encodeHexStr(data);
        Logger.e("receiver", s);
        int length = data.length;
        if (length > 7) {
            byte[] cmdIdentifier = new byte[]{data[6], data[7]};
            int cmdIdentifierInt = getIntFrom2ByteArray(cmdIdentifier);
            dispatcherState(cmdIdentifierInt, data, length, s, currentOBDSerialNum, mParseCallback);
        }
    }

    private void dispatcherState(int cmdIdentifierText, byte[] data, int length, String receiver,
                                 int serilalOBDNum, IParseResultCallback mParseCallback) {
        CommandBleDbBean cmd = new CommandBleDbBean();
        cmd.setTime(System.currentTimeMillis());
        if (0x0295 == cmdIdentifierText) {
            // 远程控制
            if (length > 5) {
                // byte[] serialNumByte = new byte[]{data[8], data[9]};
                // int serialNum = getIntFrom2ByteArray(serialNumByte);
                // if (mControlCmds.containsKey(serialNum)) {
                // ControlResult cR = mControlCmds.remove(serialNum);
                // cR.setCmdEndTime(String.valueOf(System.currentTimeMillis()));
                // cR.setRetValue(receiver);
                // String s = gson.toJson(cR);
                // Logger.e("control", s);
                // cmd.setType(4);
                // cmd.setData(s);
                // byte[] bytes = ConvertUtil.getBytes(cR.getJson());
                // int result = ConvertUtil.getIntFromByte(data[10]);
                // mParseCallback.onContrlResult(result);
                // }

                if (mControlCmds.size() == 1 && mControlCmds.peek() != null) {
                    ControlResult cR = mControlCmds.poll();
                    int result = ConvertUtil.getIntFromByte(data[length - 7]);
                    cR.setCmdEndTime(String.valueOf(System.currentTimeMillis()));
                    // cR.setRetValue(receiver);
                    cR.setRetValue(String.valueOf(result));
                    String s = new Gson().toJson(cR);
                    Logger.e("data", s);
                    byte[] sendBytes = HexUtil.hexStringToByteArray(cR.getJson());
                    String sendCode = "";
                    if (sendBytes.length > 16) {
                        int codeLength = ConvertUtil.getIntFromByte(sendBytes[16]);
                        byte[] codeByte = new byte[codeLength];
                        System.arraycopy(sendBytes, 17, codeByte, 0, codeLength);
                        sendCode = ConvertUtil.bytesToString(codeByte);
                        Logger.e("sendCode", sendCode);
                    }
                    parseEnd(cmd, s, serilalOBDNum, 4, sendCode, result, mParseCallback);
//                    mParseCallback.onContrlResult(result, sendCode);
                } else {
                    Logger.e("有多条控制指令没有处理:" + mControlCmds.size());
                }
                mControlCmds.clear();
            }
        } else if (0x0283 == cmdIdentifierText) {
            // 回复6001应答
            // gps
            parseEnd(cmd, receiver, serilalOBDNum, 1, "", -1, mParseCallback);
        } else if (0x0A07 == cmdIdentifierText) {
            // 回复6001应答
            // 状态读取
            parseEnd(cmd, receiver, serilalOBDNum, 2, "", -1, mParseCallback);
        } else if (0x0299 == cmdIdentifierText) {
            // 回复6001应答
            // 4.2.5 电量,总里程和续航里程,RPM
            parseEnd(cmd, receiver, serilalOBDNum, 3, "", -1, mParseCallback);
        } else if (0x6001 == cmdIdentifierText) {
            // 通用应答
            parseResponse(data, length, mParseCallback);
        }
    }

    /**
     * @param cmd
     * @param receiver
     * @param serilalOBDNum
     * @param type           1:gps,2:status,3:car,4:控制结果
     * @param mParseCallback
     */
    private void parseEnd(CommandBleDbBean cmd, String receiver, int serilalOBDNum, int type,
                          String sendCode, int controlResultCode, IParseResultCallback mParseCallback) {
        // 6001通用应答
        cmd.setType(type);
        cmd.setData(receiver);
        mParseCallback.parseEnd(cmd, sendCode, controlResultCode);
        mParseCallback.onSendNormalCmd(serilalOBDNum);
    }

    /**
     * 解析通用应答
     *
     * @param data
     * @param length
     */
    private void parseResponse(byte[] data, int length, IParseResultCallback mParseCallback) {
        if (length > 10) {
            int state = ConvertUtil.getIntFromByte(data[10]);
            if (state == 0x00) {
                // 成功
                mParseCallback.onCancleRepeatSend();
            }
        }
    }

    /**
     * 重置本地所有变量
     */
    public void reSet() {
        clearCmds();
        clearLastQuen();
        currentOBDSerialNum = -1;
    }

    /**
     * 清空已有的控制指令
     */
    public void clearCmds() {
        if (!mControlCmds.isEmpty()) {
            mControlCmds.clear();
        }
    }

    private void clearLastQuen() {
        if (!lastQuen.isEmpty()) {
            lastQuen.clear();
        }
    }

    /**
     * 获取已发送控制指令但是未返回指令集合
     *
     * @return
     */
    public LinkedBlockingDeque<ControlResult> getmControlCmds() {
        return mControlCmds;
    }
}
